package dungeons;

import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;


import squares.Direction;
import squares.IllegalSquareException;
import squares.RockSquare;
import squares.Square;
import squares.TransparentSquare;
import borders.Door;
import borders.Open;
import borders.Wall;
/**
 * this plateau is a special kind of dungeon that only consist of squares only in the x and y direction.
 * @invar
 * 		the maximum range of the 2 x and y directions of the dimension are all bigger then zero and smaller then Long.MAX_VALUE
 * 		the maximum range of the z direction is zero
 * 		 | maxX > 0 && maxY > 0 && maxZ == 0
 * @version 1.0
 * @author Gilles Groven & Jochen Tombal
 * @param <E>
 * 			this parameter represents the kind of square this dungeon contains.
 */

public class Plateau<E extends Square> extends SingleDungeon<E> {
	
	/**
	 * Creates a Plateau as an instance of Dungeon limited to a maximum value
	 * in the x and y direction and a 0 maximum value for the z-direction.
	 * 
	 *@post the max z value of this dungeon is zero.
	 * 		| new.getmaxZ == 0
	 */
	public Plateau(long x, long y) throws IllegalArgumentException {
		super(x, y,0);
	}
	/**
	 * Creates a Plateau, when there are no arguments given by the user, as an instance of Dungeon
	 * limited to a maximum value 10 in the x and y direction and a maximum value zero for the z-direction.
	 * 
	 *@post the max x value of this dungeon is 10.
	 * 		| new.getmaxX == 10
	 * @post the max y value of this dungeon is 10.
	 * 		| new.getmaxY == 10
	 * @post the max z value of this dungeon is zero.
	 * 		| new.getmaxZ == 0
	 */
	public Plateau(){
		super(10,10,0);
	}

	/**
	 *@post
	 * 		the given square is added on the given position if it has no rootdungeon
	 * 		|if (getRootDungeon() != null)
	 * 		|	then getSquareAt(x,y,z) == square 
	 */
	@Override
	public void addSquare(E square, long x, long y, long z)
			throws IllegalSquareException, IllegalArgumentException {
		if (square == null)
			throw new IllegalSquareException("invalid square");
		else if (x > getMaxX() || y > getMaxY() || z > getMaxZ())
			throw new IllegalArgumentException(
					"The coordinate is out of range for this Plateau.");
		else if (x < 0 || y < 0 || z < 0)
			throw new IllegalArgumentException(
					"one or more coordinates are smaller then zero.");
		else if (getSquares().contains(square))
			throw new IllegalSquareException(
					"this square is allready present in the plateau");
		else if (((Square) square).getSlipperyFloor()) 
			if (toManySlippery())
				throw new IllegalArgumentException(
						"there are already to many slippery squares.");
		else if (duplicateCoordinate(x, y, z))
			throw new IllegalSquareException(
					"already a square on this location.");
		else if (!this.willHaveValidBorders(square, x, y, z))
			throw new IllegalArgumentException(
					"placing here will result in bad borders.");
		if (((Square)square).isConnectedToOtherSquare()) {
			throw new IllegalSquareException(
					"the square is already connected to a other square");
		}
		if (getRootDungeon() != null)
		{
			long[] coords = new long[3];
			coords = this.getRootDungeon().getRootLocationOfDungeon(this);
			x =+ coords[0];
			y =+ coords[1];
			z =+ coords[2];
			this.getRootDungeon().addSquareToSpecifiedDungeon(this, square, x, y, z);
		}
		else if(square instanceof RockSquare)
		{
			addSquareToHashMap(square, x, y, z);
			mergeBorders(square, x, y, z);
			((RockSquare)square).adjustTemperature();
		}
		else
		{
		addSquareToHashMap(square, x, y, z);
		mergeBorders(square, x, y, z);
		((Square) square).adjustTemperatureSpace();
		((Square) square).adjustHumiditySpace();
		getSquares().add(square);
		}
		
	}

	/**
	 * this method checks if the given square, will have valid Borders with the given Surrounding Squares when added.
	 * @return
	 * 			true if the square has valid borders
	 * 			|when added
	 * 			|and When(
	 * 			| For(Direction direction: Direction.values())
	 * 			|	{
	 * 			|		 square.isValidBorder(direction,square.getBorderAt(direction))
	 * 			|	}
	 * 			| &&	getSquareAt(x,y+1,z).isValidBorder(Direction.SOUTH,getSquareAt(x,y+1,z).getBorderAt(Direction.SOUTH)) && 
	 * 			|		getSquareAt(x,y-1,z).isValidBorder(Direction.NORTH,getSquareAt(x,y-1,z).getBorderAt(Direction.NORTH)) &&
	 * 			|	 	getSquareAt(x+1,y,z).isValidBorder(Direction.WEST,getSquareAt(x+1,y,z).getBorderAt(Direction.WEST)) && 
	 * 			|		getSquareAt(x-1,y,z).isValidBorder(Direction.EAST,getSquareAt(x-1,y,z).getBorderAt(Direction.EAST))
	 * 			|   )
	 * 			|	then result == true
	 * 			|else  result == false
	 */

	
	@Override
	protected boolean willHaveValidBorders(E square, long x, long y, long z) {
		
		E northSquare = getSquareAt(x,y+1,z);
		E southSquare = getSquareAt(x,y-1,z);
		E eastSquare = getSquareAt(x+1,y,z);
		E westSquare = getSquareAt(x-1,y,z);

		
		if(square instanceof TransparentSquare)
		{
			if(northSquare != null)
			{
				if(northSquare.getBorderAt(Direction.SOUTH) instanceof Wall)
				{
					return false;
				}
				if(northSquare instanceof TransparentSquare)
				{
					if ((((Square) square).getBorderAt(Direction.NORTH) instanceof Door && (northSquare.getBorderAt(Direction.SOUTH) instanceof Door || northSquare.getBorderAt(Direction.NORTH) instanceof Door)) || 
							(northSquare.getBorderAt(Direction.SOUTH) instanceof Door && (((Square) square).getBorderAt(Direction.NORTH) instanceof Door || ((Square) square).getBorderAt(Direction.SOUTH) instanceof Door))
							|| (((Square) square).getBorderAt(Direction.SOUTH) instanceof Open && northSquare.getBorderAt(Direction.NORTH) instanceof Open))
					{
					}
					else
					{
						return false;
					}
				}
				if(northSquare.getBorderAt(Direction.SOUTH) instanceof Open && ((Square) square).getBorderAt(Direction.NORTH) instanceof Door && !(northSquare instanceof TransparentSquare))
				{
					if(northSquare.toManyDoors(Direction.SOUTH))
						return false;
				}
			}

			if(southSquare != null)
			{
				if(southSquare.getBorderAt(Direction.NORTH) instanceof Wall)
				{
					return false;
				}
				if(southSquare instanceof TransparentSquare)
				{
					if ((southSquare.getBorderAt(Direction.NORTH) instanceof Door && (((Square) square).getBorderAt(Direction.SOUTH) instanceof Door || ((Square) square).getBorderAt(Direction.NORTH) instanceof Door)) || 
							(((Square) square).getBorderAt(Direction.SOUTH) instanceof Door && (southSquare.getBorderAt(Direction.NORTH) instanceof Door || southSquare.getBorderAt(Direction.SOUTH) instanceof Door))
							|| (southSquare.getBorderAt(Direction.SOUTH) instanceof Open && ((Square) square).getBorderAt(Direction.NORTH) instanceof Open))
					{
						
					}
					else
					{
						return false;
					}
				}
				if(southSquare.getBorderAt(Direction.NORTH) instanceof Open && ((Square) square).getBorderAt(Direction.SOUTH) instanceof Door && !(southSquare instanceof TransparentSquare))
				{
					if(southSquare.toManyDoors(Direction.NORTH))
						return false;
				}
			}

			if(eastSquare != null)
			{
				if( eastSquare.getBorderAt(Direction.WEST) instanceof Wall)
				{
					return false;
				}
				if( eastSquare instanceof TransparentSquare)
				{
					if ((((Square) square).getBorderAt(Direction.EAST) instanceof Door && (eastSquare.getBorderAt(Direction.WEST) instanceof Door || eastSquare.getBorderAt(Direction.EAST) instanceof Door)) || 
							(eastSquare.getBorderAt(Direction.WEST) instanceof Door && (((Square) square).getBorderAt(Direction.EAST) instanceof Door || ((Square) square).getBorderAt(Direction.WEST) instanceof Door))
							|| (((Square) square).getBorderAt(Direction.WEST) instanceof Open && eastSquare.getBorderAt(Direction.EAST) instanceof Open))
					{
						
					}
					else
					{
						return false;
					}
				}
				if(eastSquare.getBorderAt(Direction.WEST) instanceof Open && ((Square) square).getBorderAt(Direction.EAST) instanceof Door && !(eastSquare instanceof TransparentSquare))
				{
					if(eastSquare.toManyDoors(Direction.WEST))
						return false;
				}
			}

			if( westSquare != null)
			{
				if(westSquare.getBorderAt(Direction.EAST) instanceof Wall)
				{
					return false;
				}
				if( westSquare instanceof TransparentSquare)
				{
					if ((((Square) square).getBorderAt(Direction.WEST) instanceof Door && (westSquare.getBorderAt(Direction.EAST) instanceof Door || westSquare.getBorderAt(Direction.WEST) instanceof Door)) || 
							(westSquare.getBorderAt(Direction.EAST) instanceof Door && (((Square) square).getBorderAt(Direction.WEST) instanceof Door || ((Square) square).getBorderAt(Direction.EAST) instanceof Door))
							|| (((Square) square).getBorderAt(Direction.EAST) instanceof Open && westSquare.getBorderAt(Direction.WEST) instanceof Open))
					{
						
					}
					else
					{
						return false;
					}
				}
				if(westSquare.getBorderAt(Direction.EAST) instanceof Open && ((Square) square).getBorderAt(Direction.WEST) instanceof Door && !(westSquare instanceof TransparentSquare))
				{
					if(westSquare.toManyDoors(Direction.EAST))
						return false;
				}
			}
		}
		else if(square instanceof RockSquare)
		{
			if(northSquare != null)
			{
				if(northSquare instanceof TransparentSquare)
				{
					return false;
				}
				else
				{
					if(northSquare.getBorderAt(Direction.SOUTH) instanceof Door)
					{
						int doors = 0;
						for(Direction direction: Direction.values())
						{if(northSquare.getBorderAt(direction) instanceof Door)
							doors++;
						}
						if(doors == 1)
						{
							return false;
						}
					}
				}
			}
			if(southSquare != null)
			{
				if(southSquare instanceof TransparentSquare)
				{
					return false;
				}
				else
				{
					if(southSquare.getBorderAt(Direction.NORTH) instanceof Door)
					{
						int doors = 0;
						for(Direction direction: Direction.values())
						{if(southSquare.getBorderAt(direction) instanceof Door)
							doors++;
						}
						if(doors == 1)
						{
							return false;
						}
					}
				}
			}
			if(eastSquare != null)
			{
				if(eastSquare instanceof TransparentSquare)
				{
					return false;
				}
				else
				{
					if(eastSquare.getBorderAt(Direction.EAST) instanceof Door)
					{
						int doors = 0;
						for(Direction direction: Direction.values())
						{if(eastSquare.getBorderAt(direction) instanceof Door)
							doors++;
						}
						if(doors == 1)
						{
							return false;
						}
					}
				}
			}
			if(westSquare != null)
			{
				if(westSquare instanceof TransparentSquare)
				{
					return false;
				}
				else
				{
					if(westSquare.getBorderAt(Direction.WEST) instanceof Door)
					{
						int doors = 0;
						for(Direction direction: Direction.values())
						{if(westSquare.getBorderAt(direction) instanceof Door)
							doors++;
						}
						if(doors == 1)
						{
							return false;
						}
					}
				}
			}
		}
		else
		{
			int doors = 0;
			if(eastSquare != null)
			{
				if(((Square) square).getBorderAt(Direction.EAST) instanceof Wall)
				{
					if(eastSquare instanceof TransparentSquare)
						return false;
					if(eastSquare.getBorderAt(Direction.WEST) instanceof Door)
							{
								int otherDoor = 0;
								for(Direction direction:Direction.values())
								{
									if(eastSquare.getBorderAt(direction) instanceof Door)
										otherDoor++;
								}
								if(otherDoor == 1)
									return false;
							}
				}
				else if(((Square) square).getBorderAt(Direction.EAST) instanceof Open)
				{
					if(eastSquare.getBorderAt(Direction.WEST) instanceof Door)
					{
						doors++;
					}
				}
				else if(((Square) square).getBorderAt(Direction.EAST) instanceof Door)
				{
					if(eastSquare.getBorderAt(Direction.WEST) instanceof Wall)
					{
						
					}
					else if(eastSquare.getBorderAt(Direction.WEST) instanceof Door)
					{
						doors++;
					}
					else if(eastSquare.getBorderAt(Direction.WEST) instanceof Open)
					{
						if( eastSquare instanceof TransparentSquare)
						{
							if(!(eastSquare.getBorderAt(Direction.WEST) instanceof Door))
							{
								return false;
							}
							else
							{
								doors++;
							}
						}
						else if(eastSquare.toManyDoors(Direction.WEST))
						{
							return false;
						}
					}
				}
			}
			if(westSquare != null)
			{
				if(((Square) square).getBorderAt(Direction.WEST) instanceof Wall)
				{
					if(westSquare instanceof TransparentSquare)
						return false;
					if(westSquare.getBorderAt(Direction.EAST) instanceof Door)
							{
								int otherDoor = 0;
								for(Direction direction:Direction.values())
								{
									if(westSquare.getBorderAt(direction) instanceof Door)
										otherDoor++;
								}
								if(otherDoor == 1)
									return false;
							}
				}
				else if(((Square) square).getBorderAt(Direction.WEST) instanceof Open)
				{
					if(westSquare.getBorderAt(Direction.EAST) instanceof Door)
					{
						doors++;
					}
				}
				else if(((Square) square).getBorderAt(Direction.WEST) instanceof Door)
				{
					if(westSquare.getBorderAt(Direction.EAST) instanceof Wall)
					{
						
					}
					else if(westSquare.getBorderAt(Direction.EAST) instanceof Door)
					{
						doors++;
					}
					else if(westSquare.getBorderAt(Direction.EAST) instanceof Open)
					{
						if( westSquare instanceof TransparentSquare)
						{
							if(!(westSquare.getBorderAt(Direction.WEST) instanceof Door))
							{
								return false;
							}
							else
							{
								doors++;
							}
						}
						else if(westSquare.toManyDoors(Direction.EAST))
						{
							return false;
						}
					}
				}
			}
			if(northSquare != null)
			{
				if(((Square) square).getBorderAt(Direction.NORTH) instanceof Wall)
				{
					if(northSquare instanceof TransparentSquare)
						return false;
					if(northSquare.getBorderAt(Direction.SOUTH) instanceof Door)
							{
								int otherDoor = 0;
								for(Direction direction:Direction.values())
								{
									if(northSquare.getBorderAt(direction) instanceof Door)
										otherDoor++;
								}
								if(otherDoor == 1)
									return false;
							}
				}
				else if(((Square) square).getBorderAt(Direction.NORTH) instanceof Open)
				{
					if(northSquare.getBorderAt(Direction.SOUTH) instanceof Door)
					{
						doors++;
					}
				}
				else if(((Square) square).getBorderAt(Direction.NORTH) instanceof Door)
				{
					if(northSquare.getBorderAt(Direction.SOUTH) instanceof Wall)
					{
						
					}
					else if(northSquare.getBorderAt(Direction.SOUTH) instanceof Door)
					{
						doors++;
					}
					else if(northSquare.getBorderAt(Direction.SOUTH) instanceof Open)
					{
						if(northSquare instanceof TransparentSquare)
						{
							if(!(northSquare.getBorderAt(Direction.NORTH) instanceof Door))
							{
								return false;
							}
							else
							{
								doors++;
							}
						}
						else if(northSquare.toManyDoors(Direction.SOUTH))
						{
							return false;
						}
					}
				}
			}
			if(southSquare != null)
			{
				if(((Square) square).getBorderAt(Direction.SOUTH) instanceof Wall)
				{
					if(southSquare instanceof TransparentSquare)
						return false;
					if(southSquare.getBorderAt(Direction.NORTH) instanceof Door)
							{
								int otherDoor = 0;
								for(Direction direction:Direction.values())
								{
									if(southSquare.getBorderAt(direction) instanceof Door)
										otherDoor++;
								}
								if(otherDoor == 1)
									return false;
							}
				}
				else if(((Square) square).getBorderAt(Direction.SOUTH) instanceof Open)
				{
					if(southSquare.getBorderAt(Direction.NORTH) instanceof Door)
					{
						doors++;
					}
				}
				else if(((Square) square).getBorderAt(Direction.SOUTH) instanceof Door)
				{
					if(southSquare.getBorderAt(Direction.NORTH) instanceof Wall)
					{
						
					}
					else if(southSquare.getBorderAt(Direction.NORTH) instanceof Door)
					{
						doors++;
					}
					else if(southSquare.getBorderAt(Direction.NORTH) instanceof Open)
					{
						if( southSquare instanceof TransparentSquare)
						{
							if(!(southSquare.getBorderAt(Direction.SOUTH) instanceof Door))
							{
								return false;
							}
							else
							{
								doors++;
							}
						}
						else if(southSquare.toManyDoors(Direction.NORTH))
						{
							return false;
						}
					}
				}
			}
			if(doors > 3)
				return false;
		}
	return true;
	}

	/**
	 * An iterator for returning all the squares of a dungeon one at a time.
	 * @return	returns the iterator of this dungeon 
	 */

	@Override
	public Iterator<E> iterator() {
		 return new Iterator<E>() {
			 	/**
	    	  	 * @return 	true if there is still a next square in the enumeration
	    	  	 */
	            public boolean hasNext() {
	            	if(y == null)
	            	{
	            		while(x.hasNext())
	            		{
	            			xValue = x.next();
	            			y = getDimensions().get(xValue).keySet().iterator();
	            			if(y.hasNext())
	            				return true;
	            		}
	            	}
	            	
	            	if(y.hasNext())
	            	{
	            		return true;
	            	}
	            	else if(x.hasNext())
	            	{
	            		return true;
	            	}
	            	else
	            	{
	            		return false;
	            	}
	            	
	            }
	            	
	           
	            /**
	             * @return the next square in the iterator
	             * @throws 	NoSuchElementException	
	             * 			if there are no more squares in the enumeration.
	             * 			|!hasNext() 
	             */ 			
	            public E next() throws NoSuchElementException {
	                if (!hasNext())
	                    throw new NoSuchElementException();
	                if(y.hasNext())
	                {
	                	long yValue = y.next();
	                	long zValue = 0;
	                	return getDimensions().get(xValue).get(yValue).get(zValue);
	                }
	                else
	                {
	                	xValue = x.next();
	                	long zValue = 0;
	                	y = getDimensions().get(xValue).keySet().iterator();
	                	return getDimensions().get(xValue).get(y.next()).get(zValue);
	                }
	                
	               
	                	
	            }
	            /**
	             *@throws UnsupportedOperationException
	             *		indicates that the requested operation, remove(), is not supported
	             */

	            public void remove() throws UnsupportedOperationException {
	                throw new UnsupportedOperationException();
	            }

	            private Iterator<Long> x = getDimensions().keySet().iterator();
	            private Iterator<Long> y = null;
	            private long xValue;
	        };
	}
	@Override
	/**
	 * @return 
	 * 		| for each E square of this.getAllSquares()
	 * 		| 	if( square instanceof RockSquare && square.getTemperature() >= 200
	 * 		| 		then result.contains(square);
	 */
	public HashSet<RockSquare> getRocksOf200Degrees() {
		Iterator<E> it = this.iterator();
		
		HashSet<RockSquare> rocks = new HashSet<RockSquare>();
		while(it.hasNext())
		{
			
			Object squareCheck = it.next();
			if(squareCheck instanceof RockSquare)
			{
				RockSquare square = (RockSquare) squareCheck;
				if(square.getTemperature() >= 200)
					rocks.add(square);
			}
		}
		return rocks;
	}
	//TODO -> mogelijkheid voor uitbreiding
//	public<T extends Square> HashSet<T> getObjectsWhere(String ... parseString)
//	{
//		parseString.
//	
//	}
	

	
	
}
